package com.wenx.v3system.modular.platform.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wenx.v3core.error.BusinessException;
import com.wenx.v3dynamicdatasourcestarter.config.DynamicDataSourceConfig;
import com.wenx.v3system.modular.platform.domain.dto.PlatformLoginResult;
import com.wenx.v3system.modular.platform.domain.dto.PlatformUserDto;
import com.wenx.v3system.modular.platform.domain.po.PlatformUser;
import com.wenx.v3secure.enums.PlatformPermission;
import com.wenx.v3system.modular.platform.mapper.PlatformUserMapper;
import com.wenx.v3system.modular.platform.service.PlatformAuthService;
import com.wenx.v3system.modular.platform.service.PlatformUserService;
import com.wenx.v3system.modular.oauth.prop.OAuth2ClientProp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.*;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.util.List;
import java.util.Map;

/**
 * 平台认证服务实现类
 * 使用RS256算法实现JWT认证，提供更高的安全性
 * 
 * @author wenx
 */
@Slf4j
@Service
@DS(DynamicDataSourceConfig.PLATFORM_DATASOURCE)
public class PlatformAuthServiceImpl extends ServiceImpl<PlatformUserMapper, PlatformUser> implements PlatformAuthService {

    private final PlatformUserService platformUserService;
    private final OAuth2ClientProp oauth2ClientProp;
    private final RestTemplate loadBalancedRestTemplate;
    private final JwtDecoder jwtDecoder;

    /**
     * 构造函数，注入OAuth2客户端配置
     */
    public PlatformAuthServiceImpl(
            PlatformUserService platformUserService,
            OAuth2ClientProp oauth2ClientProp,
            @Qualifier("oauth2LoadBalancedRestTemplate") RestTemplate loadBalancedRestTemplate,
            JwtDecoder jwtDecoder) {
        this.platformUserService = platformUserService;
        this.oauth2ClientProp = oauth2ClientProp;
        this.loadBalancedRestTemplate = loadBalancedRestTemplate;
        this.jwtDecoder = jwtDecoder;
    }

    @Override
    public PlatformLoginResult login(String username, String password) {
        // 第一步：使用client_credentials获取系统级访问令牌
        String systemAccessToken = getSystemAccessToken();
        
        // 第二步：使用系统令牌调用自定义认证端点验证用户凭据
        Map<String, Object> authResult = validateUserCredentials(systemAccessToken, username, password);
        if (authResult == null || !Boolean.TRUE.equals(authResult.get("success"))) {
            String errorMsg = authResult != null ? (String) authResult.get("message") : "用户认证失败";
            throw new BusinessException("用户认证失败: " + errorMsg);
        }
        
        // 第三步：从认证结果中获取用户token信息
        String accessToken = (String) authResult.get("access_token");
        String refreshToken = (String) authResult.get("refresh_token");
        String tokenType = authResult.get("token_type") != null ? (String) authResult.get("token_type") : "Bearer";
        Long expiresIn = parseExpiresIn(authResult.get("expires_in"));
        
        if (StrUtil.isBlank(accessToken)) {
            throw new BusinessException("认证成功但未返回有效的访问令牌");
        }

        // 第四步：组装平台侧用户信息与权限
        PlatformUserDto userDto = platformUserService.getUserByUsername(username);
        if (userDto == null) {
            throw new BusinessException("平台用户不存在或已被禁用");
        }
        List<String> permissions = platformUserService.getUserPermissions(userDto.getId());
        List<String> menuCodes = platformUserService.getUserMenuCodes(userDto.getId());
        List<String> roleCodes = platformUserService.getUserRoles(userDto.getId());

        return PlatformLoginResult.builder()
                .accessToken(accessToken)
                .refreshToken(refreshToken)
                .tokenType(tokenType)
                .expiresIn(expiresIn)
                .user(userDto)
                .permissions(permissions)
                .menuCodes(menuCodes)
                .roleCodes(roleCodes)
                .build();
    }


    @Override
    public void logout(String token) {
        // 使用配置提供的完整 Revoke URL
        String revokeUrl = oauth2ClientProp.getRevokeTokenUrl();

        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("token", token);
        params.add("client_id", oauth2ClientProp.getClientId());
        params.add("client_secret", oauth2ClientProp.getClientSecret());

        // 撤销令牌
        postFormForMap(revokeUrl, params);
    }

    @Override
    public PlatformLoginResult refreshToken(String refreshToken) {
        // 使用配置提供的完整 Token URL
        String tokenUrl = oauth2ClientProp.getTokenUrl();
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("grant_type", "refresh_token");
        params.add("refresh_token", refreshToken);
        params.add("client_id", oauth2ClientProp.getClientId());
        params.add("client_secret", oauth2ClientProp.getClientSecret());

        Map<String, Object> tokenData = postFormForMap(tokenUrl, params);
        if (tokenData == null || tokenData.get("access_token") == null) {
            throw new BusinessException("刷新失败：未返回有效的access_token");
        }

        String accessToken = (String) tokenData.get("access_token");
        String newRefreshToken = (String) tokenData.get("refresh_token");
        String tokenType = tokenData.get("token_type") != null ? (String) tokenData.get("token_type") : "Bearer";
        Long expiresIn = parseExpiresIn(tokenData.get("expires_in"));

        // 调用认证服务获取用户信息
        Map<String, Object> userInfo = fetchUserInfoFromAuth(accessToken);
        String username = resolveUsername(userInfo);
        if (StrUtil.isBlank(username)) {
            throw new BusinessException("刷新失败：无法解析用户名");
        }

        PlatformUserDto userDto = platformUserService.getUserByUsername(username);
        if (userDto == null) {
            throw new BusinessException("平台用户不存在或已被禁用");
        }
        List<String> permissions = platformUserService.getUserPermissions(userDto.getId());
        List<String> menuCodes = platformUserService.getUserMenuCodes(userDto.getId());
        List<String> roleCodes = platformUserService.getUserRoles(userDto.getId());

        return PlatformLoginResult.builder()
                .accessToken(accessToken)
                .refreshToken(newRefreshToken)
                .tokenType(tokenType)
                .expiresIn(expiresIn)
                .user(userDto)
                .permissions(permissions)
                .menuCodes(menuCodes)
                .roleCodes(roleCodes)
                .build();
    }



    @Override
    public PlatformUserDto validateToken(String token) {
        Jwt jwt = jwtDecoder.decode(token);
        Map<String, Object> userInfo = jwt.getClaims();
        String username = resolveUsername(userInfo);
        
        if (StrUtil.isBlank(username)) {
            throw new BusinessException("用户信息获取失败：无法解析用户名");
        }
        
        PlatformUserDto userDto = platformUserService.getUserByUsername(username);
        if (userDto == null) {
            throw new BusinessException("平台用户不存在或已被禁用");
        }
        
        // 补充用户的完整信息：角色、权限和菜单
        List<String> roleCodes = platformUserService.getUserRoles(userDto.getId());
        List<String> permissions = platformUserService.getUserPermissions(userDto.getId());
        List<String> menuCodes = platformUserService.getUserMenuCodes(userDto.getId());
        
        userDto.setRoleCodes(roleCodes);
        userDto.setPermissions(permissions);
        userDto.setMenuCodes(menuCodes);
        
        return userDto;
    }

    @Override
    public boolean hasPermission(Long userId, String permission) {
        List<String> userPermissions = platformUserService.getUserPermissions(userId);
        return userPermissions.stream()
                .anyMatch(p -> PlatformPermission.hasPermission(p, permission));
    }

    @Override
    public boolean hasMenuPermission(Long userId, String menuCode) {
        List<String> userMenuCodes = platformUserService.getUserMenuCodes(userId);
        return userMenuCodes.contains(menuCode);
    }

    /**
     * 通过token检查权限
     */
    public boolean hasPermissionByToken(String token, String permission) {
        PlatformUserDto userDto = validateToken(token);
        return hasPermission(userDto.getId(), permission);
    }

    /**
     * 通过token检查菜单权限
     */
    public boolean hasMenuPermissionByToken(String token, String menuCode) {
        PlatformUserDto userDto = validateToken(token);
        return hasMenuPermission(userDto.getId(), menuCode);
    }

    /**
     * 发送POST表单请求并返回Map结果
     */
    private Map<String, Object> postFormForMap(String url, MultiValueMap<String, String> params) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);
        ResponseEntity<Map> response = loadBalancedRestTemplate.postForEntity(url, request, Map.class);
        
        return response.getBody();
    }

    /**
     * 从认证服务获取用户信息
     */
    private Map<String, Object> fetchUserInfoFromAuth(String accessToken) {
        String userInfoUrl = oauth2ClientProp.getBaseUrl() + "/oauth2/userinfo";
        
        HttpHeaders headers = new HttpHeaders();
        headers.setBearerAuth(accessToken);
        
        HttpEntity<String> request = new HttpEntity<>(headers);
        ResponseEntity<Map> response = loadBalancedRestTemplate.exchange(
            userInfoUrl, HttpMethod.GET, request, Map.class);
        
        return response.getBody();
    }

    /**
     * 解析用户名
     */
    private String resolveUsername(Map<String, Object> userInfo) {
        if (userInfo == null) {
            return null;
        }
        
        // 尝试多种可能的用户名字段
        String[] usernameFields = {"username", "preferred_username", "sub", "name", "login"};
        
        for (String field : usernameFields) {
            Object value = userInfo.get(field);
            if (value != null && StrUtil.isNotBlank(value.toString())) {
                return value.toString();
            }
        }
        
        return null;
    }

    /**
     * 获取系统级访问令牌（使用client_credentials模式）
     */
    private String getSystemAccessToken() {
        String tokenUrl = oauth2ClientProp.getTokenUrl();
        
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("grant_type", "client_credentials");
        params.add("client_id", oauth2ClientProp.getClientId());
        params.add("client_secret", oauth2ClientProp.getClientSecret());
        params.add("scope", "read write"); // 添加必要的scope
        
        Map<String, Object> tokenData = postFormForMap(tokenUrl, params);
        if (tokenData == null || tokenData.get("access_token") == null) {
            throw new BusinessException("获取系统访问令牌失败：未返回有效的access_token");
        }
        
        return (String) tokenData.get("access_token");
    }
    
    /**
     * 使用系统令牌验证用户凭据
     */
    private Map<String, Object> validateUserCredentials(String systemToken, String username, String password) {
        String validateUrl = oauth2ClientProp.getBaseUrl() + "/api/auth/validate";
        
        HttpHeaders headers = new HttpHeaders();
        headers.setBearerAuth(systemToken);
        headers.setContentType(MediaType.APPLICATION_JSON);
        
        // 为平台用户添加前缀，确保CompositeUserDetailsService能正确路由
        String platformUsername = "platform:" + username;
        
        // 构建请求体
        Map<String, String> requestBody = Map.of(
            "username", platformUsername,
            "password", password
        );
        
        HttpEntity<Map<String, String>> request = new HttpEntity<>(requestBody, headers);
        ResponseEntity<Map> response = loadBalancedRestTemplate.postForEntity(validateUrl, request, Map.class);
        
        return response.getBody();
    }

    /**
     * 解析过期时间
     */
    private Long parseExpiresIn(Object expiresIn) {
        if (expiresIn == null) {
            return 86400L; // 默认24小时
        }
        
        if (expiresIn instanceof Number) {
            return ((Number) expiresIn).longValue();
        }
        
        try {
            return Long.parseLong(expiresIn.toString());
        } catch (NumberFormatException e) {
            return 86400L; // 默认24小时
        }
    }
}